import math
import random
from collections import Counter


def load_iris_data(file_path='iris.data'):
    """
    从指定路径加载Iris数据集，并返回特征-标签对列表。

    参数:
        file_path (str): 数据文件的路径，默认为 'iris.data'

    返回:
        list of tuple: 每个元组包含一个特征列表和一个对应的标签字符串。
    """
    data = []
    try:
        with open(file_path, 'r') as f:
            lines = f.readlines()
            for line in lines:
                stripped_line = line.strip()
                if stripped_line:  # 跳过空行
                    row = stripped_line.split(',')
                    # 使用列表推导式将特征值转换为浮点数，更简洁高效
                    features = [float(x) for x in row[:-1]]
                    label = row[-1]
                    data.append((features, label))
    except FileNotFoundError:
        print(f"Error: 文件 {file_path} 未找到，请检查文件路径。")
    except ValueError:
        print("Error: 文件中存在无法转换为浮点数的值，请检查数据格式。")
    return data


def euclidean_distance(point1, point2):
    """
    计算两个点之间的欧氏距离。

    参数:
        point1 (list or tuple): 第一个点的坐标
        point2 (list or tuple): 第二个点的坐标

    返回:
        float: 两点间的欧氏距离
    """
    # 使用生成器表达式计算距离，避免创建不必要的中间列表，节省内存
    return math.sqrt(sum((x - y) ** 2 for x, y in zip(point1, point2)))


def knn_classify(train_data, test_point, k):
    """
    使用KNN算法对测试点进行分类。

    参数:
        train_data (list of tuples): 训练数据集，每个元组包含特征列表和标签
        test_point (list or tuple): 测试点的特征值
        k (int): 选择的邻居数量

    返回:
        str: 预测的类别标签
    """
    # 使用列表推导式计算所有训练点到测试点的距离和标签，比循环更Pythonic
    distances = [(euclidean_distance(features, test_point), label) for features, label in train_data]
    # 使用lambda函数指定排序的键为距离，获取最近的k个邻居
    nearest_neighbors = sorted(distances, key=lambda x: x[0])[:k]
    # 使用列表推导式提取最近邻居的标签
    labels = [label for _, label in nearest_neighbors]
    # 使用Counter统计标签出现次数，并获取出现次数最多的标签
    most_common_label = Counter(labels).most_common(1)[0][0]
    return most_common_label


def train_test_split(data, test_size=0.2):
    """
    随机划分数据集为训练集和测试集。

    参数:
        data (list): 数据集，由特征-标签对组成的列表
        test_size (float): 测试集的比例，默认是20%

    返回:
        tuple: 包含训练集和测试集的元组
    """
    random.shuffle(data)  # 随机打乱数据
    split_index = int(len(data) * (1 - test_size))
    return data[:split_index], data[split_index:]


if __name__ == "__main__":
    # 加载数据
    data = load_iris_data()
    if not data:
        print("数据加载失败，程序终止。")
    else:
        train_data, test_data = train_test_split(data)
        k = 3  # 定义最近邻的数量
        # 使用生成器表达式计算预测正确的数量，避免创建中间列表
        correct_predictions = sum(knn_classify(train_data, features, k) == label for features, label in test_data)
        accuracy = correct_predictions / len(test_data)
        print(f"KNN分类准确率: {accuracy * 100:.2f}%")